{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Example Go Wrapper for Seldon Core\n",
    "\n",
    "This notebook goes through the steps to test the Go wrapper. This is presently an example of how to wrap code to run in Go in Seldon. \n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Below shows example code for a REST and gRPC server in Go that handles the Seldon Core microservice API for MODELs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[34mpackage\u001b[39;49;00m main\r\n",
      "\r\n",
      "\u001b[34mimport\u001b[39;49;00m (\r\n",
      "\t\u001b[33m\"context\"\u001b[39;49;00m\r\n",
      "\t\u001b[33m\"flag\"\u001b[39;49;00m\r\n",
      "\t\u001b[33m\"fmt\"\u001b[39;49;00m\r\n",
      "\t\u001b[33m\"github.com/golang/protobuf/jsonpb\"\u001b[39;49;00m\r\n",
      "\t\u001b[33m\"github.com/gorilla/mux\"\u001b[39;49;00m\r\n",
      "\t\u001b[33m\"github.com/seldonio/seldon-core/examples/wrappers/go/pkg/api\"\u001b[39;49;00m\r\n",
      "\t\u001b[33m\"google.golang.org/grpc\"\u001b[39;49;00m\r\n",
      "\t\u001b[33m\"log\"\u001b[39;49;00m\r\n",
      "\t\u001b[33m\"net\"\u001b[39;49;00m\r\n",
      "\t\u001b[33m\"net/http\"\u001b[39;49;00m\r\n",
      "\t\u001b[33m\"os\"\u001b[39;49;00m\r\n",
      "\t\u001b[33m\"strconv\"\u001b[39;49;00m\r\n",
      ")\r\n",
      "\r\n",
      "\u001b[34mvar\u001b[39;49;00m (\r\n",
      "\tport       = flag.Int(\u001b[33m\"port\"\u001b[39;49;00m, \u001b[34m10000\u001b[39;49;00m, \u001b[33m\"The server port\"\u001b[39;49;00m)\r\n",
      "\tserverType = flag.String(\u001b[33m\"server_type\"\u001b[39;49;00m, \u001b[33m\"grpc\"\u001b[39;49;00m, \u001b[33m\"The type of server grpc or rest\"\u001b[39;49;00m)\r\n",
      ")\r\n",
      "\r\n",
      "\u001b[37m// One struct for each type of Seldon Server. Here we just create one for MODELs\u001b[39;49;00m\r\n",
      "\u001b[34mtype\u001b[39;49;00m ModelServer \u001b[34mstruct\u001b[39;49;00m {\r\n",
      "}\r\n",
      "\r\n",
      "\u001b[37m// Example Predict call with SeldonMessage proto\u001b[39;49;00m\r\n",
      "\u001b[34mfunc\u001b[39;49;00m  (s *ModelServer) Predict(ctx context.Context, m *api.SeldonMessage) (*api.SeldonMessage, \u001b[36merror\u001b[39;49;00m){\r\n",
      "\r\n",
      "\ttest := &api.SeldonMessage{\r\n",
      "\t\tStatus: &api.Status{\r\n",
      "\t\t\tCode:                 \u001b[34m0\u001b[39;49;00m,\r\n",
      "\t\t\tInfo:                 \u001b[33m\"\"\u001b[39;49;00m,\r\n",
      "\t\t\tReason:               \u001b[33m\"\"\u001b[39;49;00m,\r\n",
      "\t\t\tStatus:               \u001b[34m0\u001b[39;49;00m,\r\n",
      "\r\n",
      "\t\t},\r\n",
      "\t\tMeta: &api.Meta{\r\n",
      "\t\t\tPuid:                 \u001b[33m\"\"\u001b[39;49;00m,\r\n",
      "\t\t\tTags:                 \u001b[34mnil\u001b[39;49;00m,\r\n",
      "\t\t\tRouting:              \u001b[34mnil\u001b[39;49;00m,\r\n",
      "\t\t\tRequestPath:          \u001b[34mnil\u001b[39;49;00m,\r\n",
      "\t\t\tMetrics:              \u001b[34mnil\u001b[39;49;00m,\r\n",
      "\r\n",
      "\t\t},\r\n",
      "\t\tDataOneof:            &api.SeldonMessage_Data{\r\n",
      "\t\t\tData: &api.DefaultData{\r\n",
      "\t\t\t\tNames:                \u001b[34mnil\u001b[39;49;00m,\r\n",
      "\t\t\t\tDataOneof:            &api.DefaultData_Tensor{\r\n",
      "\t\t\t\t\tTensor: &api.Tensor{\r\n",
      "\t\t\t\t\t\tShape:                []\u001b[36mint32\u001b[39;49;00m{\u001b[34m1\u001b[39;49;00m,\u001b[34m1\u001b[39;49;00m},\r\n",
      "\t\t\t\t\t\tValues:               []\u001b[36mfloat64\u001b[39;49;00m{\u001b[34m1\u001b[39;49;00m, \u001b[34m3\u001b[39;49;00m},\r\n",
      "\t\t\t\t\t\tXXX_NoUnkeyedLiteral: \u001b[34mstruct\u001b[39;49;00m{}{},\r\n",
      "\t\t\t\t\t\tXXX_unrecognized:     \u001b[34mnil\u001b[39;49;00m,\r\n",
      "\t\t\t\t\t\tXXX_sizecache:        \u001b[34m0\u001b[39;49;00m,\r\n",
      "\t\t\t\t\t},\r\n",
      "\t\t\t\t},\r\n",
      "\r\n",
      "\t\t\t},\r\n",
      "\t\t},\r\n",
      "\r\n",
      "\t}\r\n",
      "\t\u001b[34mreturn\u001b[39;49;00m test, \u001b[34mnil\u001b[39;49;00m\r\n",
      "}\r\n",
      "\r\n",
      "\u001b[37m// Feedback template\u001b[39;49;00m\r\n",
      "\u001b[34mfunc\u001b[39;49;00m (s *ModelServer) SendFeedback(ctx context.Context, f *api.Feedback) (*api.SeldonMessage, \u001b[36merror\u001b[39;49;00m) {\r\n",
      "\t\u001b[34mreturn\u001b[39;49;00m &api.SeldonMessage{}, \u001b[34mnil\u001b[39;49;00m\r\n",
      "}\r\n",
      "\r\n",
      "\u001b[34mfunc\u001b[39;49;00m handleError(w http.ResponseWriter,msg \u001b[36mstring\u001b[39;49;00m) {\r\n",
      "\tma := jsonpb.Marshaler{}\r\n",
      "\terrJson := &api.SeldonMessage{\r\n",
      "\t\tStatus: &api.Status{\r\n",
      "\t\t\tCode:   \u001b[34m400\u001b[39;49;00m,\r\n",
      "\t\t\tInfo:   \u001b[33m\"Failed\"\u001b[39;49;00m,\r\n",
      "\t\t\tReason: msg,\r\n",
      "\t\t\tStatus: \u001b[34m1\u001b[39;49;00m,\r\n",
      "\t\t},\r\n",
      "\t}\r\n",
      "\t_ = ma.Marshal(w, errJson)\r\n",
      "}\r\n",
      "\r\n",
      "\u001b[37m// REST predict call. Extract parameter and send to Proto version\u001b[39;49;00m\r\n",
      "\u001b[34mfunc\u001b[39;49;00m RestPredict(w http.ResponseWriter, r *http.Request) {\r\n",
      "\tlog.Println(\u001b[33m\"REST Predict called\"\u001b[39;49;00m)\r\n",
      "\tma := jsonpb.Marshaler{}\r\n",
      "\tsm := &api.SeldonMessage{}\r\n",
      "\terr := r.ParseForm()\r\n",
      "\t\u001b[34mif\u001b[39;49;00m err != \u001b[34mnil\u001b[39;49;00m {\r\n",
      "\t\thandleError(w,\u001b[33m\"Failed to parse request\"\u001b[39;49;00m)\r\n",
      "\t}\r\n",
      "\tvalue := r.FormValue(\u001b[33m\"json\"\u001b[39;49;00m)\r\n",
      "\t\u001b[34mif\u001b[39;49;00m err := jsonpb.UnmarshalString(value, sm); err != \u001b[34mnil\u001b[39;49;00m {\r\n",
      "\t\tlog.Println(\u001b[33m\"Error converting JSON to proto:\"\u001b[39;49;00m, err)\r\n",
      "\t\thandleError(w,\u001b[33m\"Failed to extract json from request\"\u001b[39;49;00m)\r\n",
      "\t\t\u001b[34mreturn\u001b[39;49;00m\r\n",
      "\t}\r\n",
      "\r\n",
      "\tlog.Printf(\u001b[33m\"message is %v\\n\"\u001b[39;49;00m,sm)\r\n",
      "\tmodelServer := &ModelServer{}\r\n",
      "\tsm_resp, _ := modelServer.Predict(r.Context(),sm)\r\n",
      "\tlog.Printf(\u001b[33m\"message is %v\\n\"\u001b[39;49;00m,sm_resp)\r\n",
      "\t_ = ma.Marshal(w, sm_resp)\r\n",
      "}\r\n",
      "\r\n",
      "\u001b[37m// REST SendFeedback call. Extract parameters and send to Proto version.\u001b[39;49;00m\r\n",
      "\u001b[34mfunc\u001b[39;49;00m RestSendFeedback(w http.ResponseWriter, r *http.Request) {\r\n",
      "\tlog.Println(\u001b[33m\"REST SendFeedback called\"\u001b[39;49;00m)\r\n",
      "\tfe := &api.Feedback{}\r\n",
      "\terr := r.ParseForm()\r\n",
      "\t\u001b[34mif\u001b[39;49;00m err != \u001b[34mnil\u001b[39;49;00m {\r\n",
      "\t\thandleError(w,\u001b[33m\"Failed to parse request\"\u001b[39;49;00m)\r\n",
      "\t}\r\n",
      "\tvalue := r.FormValue(\u001b[33m\"json\"\u001b[39;49;00m)\r\n",
      "\t\u001b[34mif\u001b[39;49;00m err := jsonpb.UnmarshalString(value, fe); err != \u001b[34mnil\u001b[39;49;00m {\r\n",
      "\t\tlog.Println(\u001b[33m\"Error converting JSON to proto:\"\u001b[39;49;00m, err)\r\n",
      "\t\thandleError(w,\u001b[33m\"Failed to extract json from request\"\u001b[39;49;00m)\r\n",
      "\t\t\u001b[34mreturn\u001b[39;49;00m\r\n",
      "\t}\r\n",
      "\tlog.Printf(\u001b[33m\"message is %v\\n\"\u001b[39;49;00m,fe)\r\n",
      "\tmodelServer := &ModelServer{}\r\n",
      "\tsm_resp, _ := modelServer.SendFeedback(r.Context(),fe)\r\n",
      "\tlog.Printf(\u001b[33m\"message is %v\\n\"\u001b[39;49;00m,sm_resp)\r\n",
      "\tma := jsonpb.Marshaler{}\r\n",
      "\t_ = ma.Marshal(w, sm_resp)\r\n",
      "}\r\n",
      "\r\n",
      "\r\n",
      "\u001b[37m/*\u001b[39;49;00m\r\n",
      "\u001b[37m  Start gRPC or REST server\u001b[39;49;00m\r\n",
      "\u001b[37m */\u001b[39;49;00m\r\n",
      "\u001b[34mfunc\u001b[39;49;00m main() {\r\n",
      "\tflag.Parse()\r\n",
      "\t\u001b[34mvar\u001b[39;49;00m portEnv = os.Getenv(\u001b[33m\"PREDICTIVE_UNIT_SERVICE_PORT\"\u001b[39;49;00m)\r\n",
      "\t\u001b[34mif\u001b[39;49;00m portEnv != \u001b[33m\"\"\u001b[39;49;00m {\r\n",
      "\t\tportVal,err := strconv.Atoi(portEnv)\r\n",
      "\t\t\u001b[34mif\u001b[39;49;00m err != \u001b[34mnil\u001b[39;49;00m {\r\n",
      "\t\t\tlog.Fatal(\u001b[33m\"Bad env variable PREDICTIVE_UNIT_SERVICE_PORT\"\u001b[39;49;00m)\r\n",
      "\t\t} \u001b[34melse\u001b[39;49;00m {\r\n",
      "\t\t\tport = &portVal\r\n",
      "\t\t}\r\n",
      "\t}\r\n",
      "\tlog.Printf(\u001b[33m\"Server_type: %s\\n\"\u001b[39;49;00m,*serverType)\r\n",
      "\tlog.Printf(\u001b[33m\"Port: %d\\n\"\u001b[39;49;00m,*port)\r\n",
      "\t\u001b[34mif\u001b[39;49;00m *serverType == \u001b[33m\"rest\"\u001b[39;49;00m {\r\n",
      "\t\tlog.Println(\u001b[33m\"Starting REST Server\"\u001b[39;49;00m)\r\n",
      "\t\trouter := mux.NewRouter()\r\n",
      "\t\trouter.HandleFunc(\u001b[33m\"/predict\"\u001b[39;49;00m, RestPredict).Methods(\u001b[33m\"GET\"\u001b[39;49;00m,\u001b[33m\"POST\"\u001b[39;49;00m)\r\n",
      "\t\trouter.HandleFunc(\u001b[33m\"/send-feedback\"\u001b[39;49;00m, RestSendFeedback).Methods(\u001b[33m\"GET\"\u001b[39;49;00m,\u001b[33m\"POST\"\u001b[39;49;00m)\r\n",
      "\t\tlog.Fatal(http.ListenAndServe(fmt.Sprintf(\u001b[33m\":%d\"\u001b[39;49;00m, *port), router))\r\n",
      "\t} \u001b[34melse\u001b[39;49;00m {\r\n",
      "\t\tlog.Println(\u001b[33m\"Starting gRPC Server\"\u001b[39;49;00m)\r\n",
      "\t\tflag.Parse()\r\n",
      "\t\tlis, err := net.Listen(\u001b[33m\"tcp\"\u001b[39;49;00m, fmt.Sprintf(\u001b[33m\":%d\"\u001b[39;49;00m, *port))\r\n",
      "\t\t\u001b[34mif\u001b[39;49;00m err != \u001b[34mnil\u001b[39;49;00m {\r\n",
      "\t\t\tlog.Fatalf(\u001b[33m\"failed to listen: %v\"\u001b[39;49;00m, err)\r\n",
      "\t\t}\r\n",
      "\t\t\u001b[34mvar\u001b[39;49;00m opts []grpc.ServerOption\r\n",
      "\t\tgrpcServer := grpc.NewServer(opts...)\r\n",
      "\t\tapi.RegisterModelServer(grpcServer, &ModelServer{})\r\n",
      "\t\tgrpcServer.Serve(lis)\r\n",
      "\t}\r\n",
      "\r\n",
      "\r\n",
      "}\r\n"
     ]
    }
   ],
   "source": [
    "!pygmentize server.go"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Local Docker Test\n",
    "\n",
    "For this test we will use the code as is which returns a hard-wired result. For real use you should copy this Go project to build your own components.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "docker build -t seldonio/gomodel:0.1 .\n",
      "Sending build context to Docker daemon  486.9kB\n",
      "Step 1/7 : FROM golang\n",
      "latest: Pulling from library/golang\n",
      "\n",
      "\u001b[1B55d5a1d1: Pulling fs layer \n",
      "\u001b[1B80d00ae9: Pulling fs layer \n",
      "\u001b[1Bb3117dca: Pulling fs layer \n",
      "\u001b[1Ba19181b2: Pulling fs layer \n",
      "\u001b[1B759e848f: Pulling fs layer \n",
      "\u001b[1B9eaeed19: Pulling fs layer \n",
      "\u001b[1BDigest: sha256:5825f1549d8929cb8bc9525b372668105cf6517df5cd8c7f5b5e3a51673e0e07\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[KDownloading  67.18MB/127.9MB\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[1A\u001b[1K\u001b[K\n",
      "Status: Downloaded newer image for golang:latest\n",
      " ---> 7ced090ee82e\n",
      "Step 2/7 : RUN go get -u google.golang.org/grpc\n",
      " ---> Running in 54349cbf622c\n",
      "Removing intermediate container 54349cbf622c\n",
      " ---> 870c6a74226b\n",
      "Step 3/7 : RUN go get -u github.com/gorilla/mux\n",
      " ---> Running in 38b37c710bdf\n",
      "Removing intermediate container 38b37c710bdf\n",
      " ---> e1d97f4a05a0\n",
      "Step 4/7 : WORKDIR /go/src/github.com/seldonio/seldon-core/examples/wrappers/go\n",
      " ---> Running in 177cbd8d0228\n",
      "Removing intermediate container 177cbd8d0228\n",
      " ---> 3cac5de40b35\n",
      "Step 5/7 : COPY . .\n",
      " ---> 042fc193a467\n",
      "Step 6/7 : RUN go build -o /server\n",
      " ---> Running in e367347e4512\n",
      "Removing intermediate container e367347e4512\n",
      " ---> 954b75558834\n",
      "Step 7/7 : ENTRYPOINT [ \"sh\", \"-c\", \"/server --server_type ${SERVER_TYPE:-grpc}\" ]\n",
      " ---> Running in ca5fab9640eb\n",
      "Removing intermediate container ca5fab9640eb\n",
      " ---> a0a6577db0ec\n",
      "Successfully built a0a6577db0ec\n",
      "Successfully tagged seldonio/gomodel:0.1\n"
     ]
    }
   ],
   "source": [
    "!make build_docker"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Run a REST test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "docker run -d --name \"gomodel\" -p 10000:10000 -e SERVER_TYPE='rest' --rm seldonio/gomodel:0.1\n",
      "ffe632b3eeaf06c3da6a3bd1ba00a1f036742554705d5b606a5f6338d7b4b818\n"
     ]
    }
   ],
   "source": [
    "!make test_docker_rest"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "----------------------------------------\r\n",
      "SENDING NEW REQUEST:\r\n",
      "\r\n",
      "[[4.954]]\r\n",
      "RECEIVED RESPONSE:\r\n",
      "status {\r\n",
      "}\r\n",
      "meta {\r\n",
      "}\r\n",
      "data {\r\n",
      "  tensor {\r\n",
      "    shape: 1\r\n",
      "    shape: 1\r\n",
      "    values: 1.0\r\n",
      "    values: 3.0\r\n",
      "  }\r\n",
      "}\r\n",
      "\r\n",
      "\r\n"
     ]
    }
   ],
   "source": [
    "!seldon-core-tester contract.json 0.0.0.0 10000 -p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gomodel\r\n"
     ]
    }
   ],
   "source": [
    "!docker rm -f gomodel"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Run a gRPC test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "docker run -d --name \"gomodel\" -p 10000:10000 --rm seldonio/gomodel:0.1\n",
      "be15ba3b8a729321063bb941be7501c7aec0e7d4f309fc9a95bb56481d18a616\n"
     ]
    }
   ],
   "source": [
    "!make test_docker_grpc"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "----------------------------------------\r\n",
      "SENDING NEW REQUEST:\r\n",
      "\r\n",
      "[[3.577]]\r\n",
      "RECEIVED RESPONSE:\r\n",
      "status {\r\n",
      "}\r\n",
      "meta {\r\n",
      "}\r\n",
      "data {\r\n",
      "  tensor {\r\n",
      "    shape: 1\r\n",
      "    shape: 1\r\n",
      "    values: 1.0\r\n",
      "    values: 3.0\r\n",
      "  }\r\n",
      "}\r\n",
      "\r\n",
      "\r\n"
     ]
    }
   ],
   "source": [
    "!seldon-core-tester contract.json 0.0.0.0 10000 -p --grpc --tensor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gomodel\r\n"
     ]
    }
   ],
   "source": [
    "!docker rm -f gomodel"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Test in Minikube"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!minikube start --memory 4096"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "clusterrolebinding.rbac.authorization.k8s.io/kube-system-cluster-admin created\r\n"
     ]
    }
   ],
   "source": [
    "!kubectl create clusterrolebinding kube-system-cluster-admin --clusterrole=cluster-admin --serviceaccount=kube-system:default"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "$HELM_HOME has been configured at /home/clive/.helm.\n",
      "\n",
      "Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.\n",
      "\n",
      "Please note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.\n",
      "To prevent this, run `helm init` with the --tiller-tls-verify flag.\n",
      "For more information on securing your installation see: https://docs.helm.sh/using_helm/#securing-your-helm-installation\n",
      "Happy Helming!\n"
     ]
    }
   ],
   "source": [
    "!helm init"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Waiting for deployment \"tiller-deploy\" rollout to finish: 0 of 1 updated replicas are available...\n",
      "deployment \"tiller-deploy\" successfully rolled out\n"
     ]
    }
   ],
   "source": [
    "!kubectl rollout status deploy/tiller-deploy -n kube-system"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME:   seldon-core\n",
      "LAST DEPLOYED: Thu May  9 15:47:55 2019\n",
      "NAMESPACE: seldon-system\n",
      "STATUS: DEPLOYED\n",
      "\n",
      "RESOURCES:\n",
      "==> v1/Pod(related)\n",
      "NAME                                  READY  STATUS             RESTARTS  AGE\n",
      "seldon-operator-controller-manager-0  0/1    ContainerCreating  0         0s\n",
      "\n",
      "==> v1/Secret\n",
      "NAME                                   TYPE    DATA  AGE\n",
      "seldon-operator-webhook-server-secret  Opaque  0     0s\n",
      "\n",
      "==> v1beta1/CustomResourceDefinition\n",
      "NAME                                         AGE\n",
      "seldondeployments.machinelearning.seldon.io  0s\n",
      "\n",
      "==> v1/ClusterRole\n",
      "seldon-operator-manager-role  0s\n",
      "\n",
      "==> v1/ClusterRoleBinding\n",
      "NAME                                 AGE\n",
      "seldon-operator-manager-rolebinding  0s\n",
      "\n",
      "==> v1/Service\n",
      "NAME                                        TYPE       CLUSTER-IP     EXTERNAL-IP  PORT(S)  AGE\n",
      "seldon-operator-controller-manager-service  ClusterIP  10.105.155.80  <none>       443/TCP  0s\n",
      "\n",
      "==> v1/StatefulSet\n",
      "NAME                                DESIRED  CURRENT  AGE\n",
      "seldon-operator-controller-manager  1        1        0s\n",
      "\n",
      "\n",
      "NOTES:\n",
      "NOTES: TODO\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "!helm install ../../../helm-charts/seldon-core-operator --name seldon-core --set usageMetrics.enabled=true   --namespace seldon-system"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Waiting for 1 pods to be ready...\n",
      "partitioned roll out complete: 1 new pods have been updated...\n"
     ]
    }
   ],
   "source": [
    "!kubectl rollout status deploy/seldon-controller-manager -n seldon-system"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup Ingress\n",
    "Please note: There are reported gRPC issues with ambassador (see https://github.com/SeldonIO/seldon-core/issues/473)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME:   ambassador\n",
      "LAST DEPLOYED: Thu May  9 15:48:08 2019\n",
      "NAMESPACE: default\n",
      "STATUS: DEPLOYED\n",
      "\n",
      "RESOURCES:\n",
      "==> v1/Pod(related)\n",
      "NAME                         READY  STATUS             RESTARTS  AGE\n",
      "ambassador-5b89d44544-2s79n  0/1    ContainerCreating  0         0s\n",
      "ambassador-5b89d44544-fmvgk  0/1    ContainerCreating  0         0s\n",
      "ambassador-5b89d44544-gmd8s  0/1    ContainerCreating  0         0s\n",
      "\n",
      "==> v1/ServiceAccount\n",
      "NAME        SECRETS  AGE\n",
      "ambassador  1        0s\n",
      "\n",
      "==> v1beta1/ClusterRole\n",
      "NAME        AGE\n",
      "ambassador  0s\n",
      "\n",
      "==> v1beta1/ClusterRoleBinding\n",
      "NAME        AGE\n",
      "ambassador  0s\n",
      "\n",
      "==> v1/Service\n",
      "NAME               TYPE          CLUSTER-IP     EXTERNAL-IP  PORT(S)                     AGE\n",
      "ambassador-admins  ClusterIP     10.111.86.228  <none>       8877/TCP                    0s\n",
      "ambassador         LoadBalancer  10.99.124.251  <pending>    80:32260/TCP,443:31122/TCP  0s\n",
      "\n",
      "==> v1/Deployment\n",
      "NAME        DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE\n",
      "ambassador  3        3        3           0          0s\n",
      "\n",
      "\n",
      "NOTES:\n",
      "Congratuations! You've successfully installed Ambassador.\n",
      "\n",
      "For help, visit our Slack at https://d6e.co/slack or view the documentation online at https://www.getambassador.io.\n",
      "\n",
      "To get the IP address of Ambassador, run the following commands:\n",
      "NOTE: It may take a few minutes for the LoadBalancer IP to be available.\n",
      "     You can watch the status of by running 'kubectl get svc -w  --namespace default ambassador'\n",
      "\n",
      "  On GKE/Azure:\n",
      "  export SERVICE_IP=$(kubectl get svc --namespace default ambassador -o jsonpath='{.status.loadBalancer.ingress[0].ip}')\n",
      "\n",
      "  On AWS:\n",
      "  export SERVICE_IP=$(kubectl get svc --namespace default ambassador -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')\n",
      "\n",
      "  echo http://$SERVICE_IP:\n",
      "\n"
     ]
    }
   ],
   "source": [
    "!helm install stable/ambassador --name ambassador --set crds.keep=false"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Waiting for deployment \"ambassador\" rollout to finish: 0 of 3 updated replicas are available...\n",
      "Waiting for deployment \"ambassador\" rollout to finish: 1 of 3 updated replicas are available...\n",
      "Waiting for deployment \"ambassador\" rollout to finish: 2 of 3 updated replicas are available...\n",
      "deployment \"ambassador\" successfully rolled out\n"
     ]
    }
   ],
   "source": [
    "!kubectl rollout status deployment.apps/ambassador"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "docker build -t seldonio/gomodel:0.1 .\n",
      "Sending build context to Docker daemon  543.7kB\n",
      "Step 1/7 : FROM golang\n",
      "latest: Pulling from library/golang\n",
      "\n",
      "\u001b[1B55d5a1d1: Pulling fs layer \n",
      "\u001b[1B80d00ae9: Pulling fs layer \n",
      "\u001b[1Bb3117dca: Pulling fs layer \n",
      "\u001b[1Ba19181b2: Pulling fs layer \n",
      "\u001b[1B759e848f: Pulling fs layer \n",
      "\u001b[1B9eaeed19: Pulling fs layer \n",
      "\u001b[1BDigest: sha256:5825f1549d8929cb8bc9525b372668105cf6517df5cd8c7f5b5e3a51673e0e07\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[KDownloading  35.83MB/45.34MB\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[7A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[6A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[5A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[4A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[3A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[2A\u001b[1K\u001b[K\u001b[1A\u001b[1K\u001b[K\n",
      "Status: Downloaded newer image for golang:latest\n",
      " ---> 7ced090ee82e\n",
      "Step 2/7 : RUN go get -u google.golang.org/grpc\n",
      " ---> Running in c47778d3b2ed\n",
      "Removing intermediate container c47778d3b2ed\n",
      " ---> ca1dff8faf05\n",
      "Step 3/7 : RUN go get -u github.com/gorilla/mux\n",
      " ---> Running in e8ab459e22ab\n",
      "Removing intermediate container e8ab459e22ab\n",
      " ---> ff67a9ffe7c8\n",
      "Step 4/7 : WORKDIR /go/src/github.com/seldonio/seldon-core/examples/wrappers/go\n",
      " ---> Running in b5bd80b58c56\n",
      "Removing intermediate container b5bd80b58c56\n",
      " ---> 220974fdf4f9\n",
      "Step 5/7 : COPY . .\n",
      " ---> 6cdc57523d88\n",
      "Step 6/7 : RUN go build -o /server\n",
      " ---> Running in e17c70fe376c\n",
      "Removing intermediate container e17c70fe376c\n",
      " ---> 10fd1ac9d773\n",
      "Step 7/7 : ENTRYPOINT [ \"sh\", \"-c\", \"/server --server_type ${SERVER_TYPE:-grpc}\" ]\n",
      " ---> Running in c68e836ec343\n",
      "Removing intermediate container c68e836ec343\n",
      " ---> ac119b5ba1bc\n",
      "Successfully built ac119b5ba1bc\n",
      "Successfully tagged seldonio/gomodel:0.1\n"
     ]
    }
   ],
   "source": [
    "!eval $(minikube docker-env) && make build_docker"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "seldondeployment.machinelearning.seldon.io/example-go created\r\n"
     ]
    }
   ],
   "source": [
    "!kubectl create -f resources/deployment_example.json"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Waiting for deployment \"go-deployment-go-predictor-27dfae4\" rollout to finish: 0 of 1 updated replicas are available...\n",
      "deployment \"go-deployment-go-predictor-27dfae4\" successfully rolled out\n"
     ]
    }
   ],
   "source": [
    "!kubectl rollout status deploy/go-deployment-go-predictor-27dfae4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "----------------------------------------\n",
      "SENDING NEW REQUEST:\n",
      "\n",
      "[[7.961]]\n",
      "RECEIVED RESPONSE:\n",
      "status {\n",
      "}\n",
      "meta {\n",
      "  puid: \"p4vmdh0epbh4o4vtpu469s1ecc\"\n",
      "  requestPath {\n",
      "    key: \"gomodel\"\n",
      "    value: \"seldonio/gomodel:0.1\"\n",
      "  }\n",
      "}\n",
      "data {\n",
      "  tensor {\n",
      "    shape: 1\n",
      "    shape: 1\n",
      "    values: 1.0\n",
      "    values: 3.0\n",
      "  }\n",
      "}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "!seldon-core-api-tester contract.json `minikube ip` `kubectl get svc ambassador -o jsonpath='{.spec.ports[0].nodePort}'` \\\n",
    "    example-go --namespace default -p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!minikube delete"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
